import { director, Event, instantiate, js, Layers, log, Node, Prefab, SceneAsset, warn } from 'cc';
import { BaseManager } from '../../base/BaseManager';
import { BaseGlobal, CoreModule } from '../../base/BaseModule';
import { BaseView } from '../../base/BaseView';
import { createUUID } from '../../utillity/Utillity';
import { app } from '../../XForge';
import UILoading from './UILoading';
import UIShadow from './UIShadow';
import UIToast from './UIToast';

const UIType = {
    Page: 'Page',
    Paper: 'Paper',
    Pop: 'Pop',
    Top: 'Top'
};

// 获取UI类型，返回值首字母大写
function getUITypeName(viewName: string): string {
    if (viewName.startsWith(UIManager.UIType.Page)) {
        return UIManager.UIType.Page;
    }
    if (viewName.startsWith(UIManager.UIType.Paper)) {
        return UIManager.UIType.Paper;
    }
    if (viewName.startsWith(UIManager.UIType.Pop)) {
        return UIManager.UIType.Pop;
    }
    if (viewName.startsWith(UIManager.UIType.Top)) {
        return UIManager.UIType.Top;
    }
    return '';
}

enum UIState {
    None,
    Loading,
    Showing,
    Hiding
}

const BlockEvents = [
    Node.EventType.TOUCH_START, Node.EventType.TOUCH_MOVE, Node.EventType.TOUCH_END, Node.EventType.TOUCH_CANCEL,
    Node.EventType.MOUSE_DOWN, Node.EventType.MOUSE_MOVE, Node.EventType.MOUSE_UP,
    Node.EventType.MOUSE_ENTER, Node.EventType.MOUSE_LEAVE, Node.EventType.MOUSE_WHEEL
];

class UICommand {
    public viewType: { new(): BaseView };
    public data: any;
    public onShow: () => void;
    public onHide: (data: any) => void;
    public onError: () => void;
    public onProgress: (progress: number) => void;
}

class UISource {
    public viewState = UIState.None;
    public viewComp: BaseView = null;

    public Clear(): void {
        if (this.viewComp != null) {
            this.viewComp.node.destroy();
        }
        this.viewState = UIState.None;
        this.viewComp = null;
    }
}

export type IShadow = {
    /**等待 默认0秒 */
    delay?: number,
    /**开始透明度 默认60 */
    begin?: number,
    /**结束透明度 默认180 */
    end?: number,
    /**透明变化速度 默认100 */
    speed?: number,
};

export class UIManager extends BaseManager {
    public static UIType = UIType;
    public static UICommand = UICommand;
    private static UISource = UISource;

    /**静态设置 */
    static settings: {
        /**弹窗背景遮罩的参数 */
        shadow?: IShadow,
        /**
         * 是否自动适配分辨率策略
         * - 开启后会弃用当前的适配策略，并根据实际设备分辨率与设计分辨率的比值，计算出新的适配策略(宽适配或高适配)，保证游戏区域不会被裁减只会扩边
         *   - 当实际设备分辨率「高/宽」>= 设计分辨率「高/宽」时，为宽适配
         *   - 当实际设备分辨率「高/宽」< 设计分辨率「高/宽」时，为高适配
         */
        autoFit?: boolean,
    } = {};

    ////////////////////////// shadow //////////////////////////
    private static _shadow: Node;
    private static get shadow(): Node {
        if (!this._shadow) {
            this._shadow = instantiate(app.configuration.UIShadow);
            this._shadow.parent = app.configuration.UserInterface;
            this._shadow.layer = Layers.Enum.UI_2D;
            this._shadow.active = false;
        }
        return this._shadow;
    }
    /**
     * 获取一个节点上的BaseView组件, 获取不到返回null
     */
    private static getBaseView(node: Node): BaseView {
        if (!node) return null;
        return node.components.find(component => component instanceof BaseView) as BaseView;
    }
    /**
     * 更新阴影的层级及显示
     */
    public static refreshShadow() {
        // 倒序遍历uiRoots
        const uiRoots = app.configuration.UserInterface.children;
        for (let index = uiRoots.length - 1; index >= 0; index--) {
            const uiRoot = uiRoots[index];
            if (uiRoot.name === UIType.Paper || uiRoot.name === UIType.Pop || uiRoot.name === UIType.Top) {
                // 倒序遍历uiRoot
                const children = uiRoot.children;
                for (let i = children.length - 1; i >= 0; i--) {
                    const node = children[i];
                    if (node === this.shadow) continue;

                    const com = this.getBaseView(node);
                    if (!com) continue;
                    if (!this.isShowing(com)) continue;

                    // 添加遮罩
                    if (com.shadow) {
                        const shadowSetting = Object.assign({}, UIManager.settings.shadow);
                        this.shadow.getComponent(UIShadow).init(
                            typeof shadowSetting.delay !== 'number' ? 0 : shadowSetting.delay,
                            typeof shadowSetting.begin !== 'number' ? 60 : shadowSetting.begin,
                            typeof shadowSetting.end !== 'number' ? 180 : shadowSetting.end,
                            typeof shadowSetting.speed !== 'number' ? 100 : shadowSetting.speed
                        );
                        this.shadow.layer = node.layer;
                        this.shadow.parent = uiRoot;
                        this.shadow.active = true;

                        let shadowIndex = this.shadow.getSiblingIndex();
                        let nodeIndex = node.getSiblingIndex();
                        if (shadowIndex > nodeIndex) {
                            this.shadow.setSiblingIndex(nodeIndex);
                        } else {
                            this.shadow.setSiblingIndex(nodeIndex - 1);
                        }
                        return;
                    }
                }
            }
        }

        this.shadow.active = false;
        this.shadow.getComponent(UIShadow).clear();
    }

    ////////////////////////// loading //////////////////////////
    // 记录展示加载
    private static loadingMap = new Map<number, boolean>();
    private static _loading: Node;
    private static get loading(): Node {
        if (!this._loading) {
            this._loading = instantiate(app.configuration.UILoading);
            this._loading.parent = app.configuration.UserInterface;
            this._loading.layer = Layers.Enum.UI_2D;
            this._loading.setSiblingIndex(3);
            this._loading.active = false;
        }
        return this._loading;
    }
    /**
     * 添加loading
     * @param timeout 毫秒
     */
    public static addLoading(timeout = 0) {
        UIManager.loading.active = true;
        UIManager.loading.setSiblingIndex(-1);
        UIManager.loading.getComponent(UILoading).init();
        const uuid = createUUID();
        this.loadingMap.set(uuid, true);
        if (timeout > 0) setTimeout(() => {
            this.removeLoading(uuid);
        }, timeout);
        return uuid;
    }
    /**
     * 移除loading
     * @param uuid showLoading的返回值
     * @returns 
     */
    public static removeLoading(uuid: number) {
        if (!uuid) return;
        this.loadingMap.delete(uuid);
        if (this.loadingMap.size === 0) {
            UIManager.loading.getComponent(UILoading).clear();
            UIManager.loading.active = false;
        }
    }

    ////////////////////////// toast //////////////////////////
    private static _toast: Node;
    private static get toast(): Node {
        if (!this._toast) {
            this._toast = instantiate(app.configuration.UIToast);
            this._toast.parent = app.configuration.UserInterface;
            this._toast.layer = Layers.Enum.UI_2D;
            this._loading.setSiblingIndex(-1);
        }
        return this._toast;
    }
    /**
     * 显示Toast
     * @param message 文本
     * @param timeout 持续时间(秒)，默认2秒
     */
    public static showToast(message: string, timeout?: number) {
        UIManager.toast.setSiblingIndex(-1);
        UIManager.toast.getComponent(UIToast).add({
            message, timeout
        });
    }
    /**
     * 清理Toast
     */
    public static clearToast() {
        if (!UIManager.toast) return;
        UIManager.toast.getComponent(UIToast).clear();
    }

    ////////////////////////// touch //////////////////////////
    // 全局触摸有效
    private static touchEnabled: boolean = true;
    // 记录触摸屏蔽
    private static touchMaskMap = new Map<number, boolean>();

    private static addTouchMaskListener() {
        if (!this.touchEnabled) return;
        if (this.touchMaskMap.size > 0) return;

        for (let i = 0; i < BlockEvents.length; i++) {
            app.configuration.Canvas.node.on(BlockEvents[i], this.stopPropagation, this, true);
        }
    }
    private static removeTouchMaskListener() {
        if (!this.touchEnabled) return;
        if (this.touchMaskMap.size > 0) return;

        for (let i = 0; i < BlockEvents.length; i++) {
            app.configuration.Canvas.node.off(BlockEvents[i], this.stopPropagation, this, true);
        }
    }
    private static stopPropagation(event: Event) {
        if (!this.touchEnabled || this.touchMaskMap.size > 0) {
            event.propagationStopped = true;
            if (event.type !== Node.EventType.MOUSE_MOVE) {
                log('[UIManager]', '屏蔽触摸');
            }
        }
    }

    /**
     * 添加触摸屏蔽
     * @param timeout 毫秒
     * @returns 
     */
    public static addTouchMask(timeout = 0) {
        this.addTouchMaskListener();
        const uuid = createUUID();
        this.touchMaskMap.set(uuid, true);
        if (timeout > 0) setTimeout(() => {
            this.removeTouchMask(uuid);
        }, timeout);
        return uuid;
    }

    /**
     * 移除触摸屏蔽
     * @param uuid addTouchMask的返回值
     */
    public static removeTouchMask(uuid: number) {
        if (!uuid) return;
        this.touchMaskMap.delete(uuid);
        this.removeTouchMaskListener();
    }

    /**
     * 设置触摸是否启用
     * @param enabled 是否启用
     */
    public static setTouchEnabled(enabled: boolean) {
        if (enabled) {
            this.touchEnabled = true;
            this.removeTouchMaskListener();
        } else {
            this.addTouchMaskListener();
            this.touchEnabled = false;
        }
        warn('[UIManager]', 'setTouchEnabled', this.touchEnabled);
    }

    // 存储所有模块中的UISource
    private static viewSourceMap: Map<string, Map<string, UISource>> = new Map();
    private static getViewSource(view: typeof BaseView | BaseView): UISource {
        if (view instanceof BaseView) {
            for (const [, map] of UIManager.viewSourceMap) {
                for (const [, source] of map) {
                    if (source.viewComp === view) {
                        return source;
                    }
                }
            }
        } else {
            for (const [, map] of UIManager.viewSourceMap) {
                for (const [, source] of map) {
                    if (source.viewComp instanceof view) {
                        return source;
                    }
                }
            }
        }
        return null;
    }
    public static isLoading(view: typeof BaseView | BaseView): boolean {
        const viewSource = UIManager.getViewSource(view);
        if (!viewSource) return false;
        return viewSource.viewState === UIState.Loading;
    }

    public static isShowing(view: typeof BaseView | BaseView): boolean {
        const viewSource = UIManager.getViewSource(view);
        if (!viewSource) return false;
        return viewSource.viewState === UIState.Showing;
    }

    private viewCommandList: UICommand[] = [];

    /**
     * 释放缓存
     */
    public release(): void {
        if (UIManager.viewSourceMap.has(this.module.moduleName)) {
            UIManager.viewSourceMap.get(this.module.moduleName).forEach(source => {
                source.Clear();
            });
            UIManager.viewSourceMap.delete(this.module.moduleName);
        }
    }

    private LoadScene(viewName: string, onCompleted: (scene: SceneAsset) => void, onProgress?: (progress: number) => void): void {
        const assetName = this.module instanceof BaseGlobal
            ? `global-view/resources/${viewName}`
            : `module-view/${viewName}`;

        this.module.loadAsset(assetName, SceneAsset, onCompleted, onProgress);
    }

    private LoadPrefab(viewName: string, onCompleted: (prefab: Prefab) => void, onProgress?: (progress: number) => void): void {
        const assetName = this.module instanceof BaseGlobal
            ? `global-view/resources/${viewName}`
            : `module-view/${viewName}`;

        this.module.loadAsset(assetName, Prefab, onCompleted, onProgress);
    }

    public preload(view: typeof BaseView, onCompleted?: () => void, onProgress?: (progress: number) => void): void {
        const viewName = js.getClassName(view);
        const typeName = getUITypeName(viewName);

        if (typeName === UIType.Page) {
            this.LoadScene(viewName, onCompleted, onProgress);
        } else {
            this.LoadPrefab(viewName, onCompleted, onProgress);
        }
    }

    private tryShowing: boolean = false;
    private tryShow(): void {
        this.tryShowing = true;

        if (this.viewCommandList.length === 0) {
            this.tryShowing = false;
            return;
        }
        const command = this.viewCommandList[0];

        const moduleName = this.module.moduleName;
        const viewName = js.getClassName(command.viewType);
        const typeName = getUITypeName(viewName);
        const fullName = `${moduleName}:${viewName}`;

        if (!UIManager.viewSourceMap.has(moduleName)) {
            UIManager.viewSourceMap.set(moduleName, new Map());
        }
        const viewSourceMap = UIManager.viewSourceMap.get(moduleName);

        if (!viewSourceMap.has(viewName)) {
            viewSourceMap.set(viewName, new UIManager.UISource());
        }
        const viewSource = viewSourceMap.get(viewName);

        // 可直接使用的状态
        if (viewSource.viewComp != null) {
            setTimeout(() => {
                if (this.viewCommandList.indexOf(command) === -1) return;
                this.viewCommandList.splice(this.viewCommandList.indexOf(command), 1);
                this.tryShow();

                BaseView.innerShow(viewSource.viewComp, command.data, command.onShow, command.onHide).then(() => {
                    UIManager.refreshShadow();
                });
            });
        } else {
            // 需要下载
            viewSource.viewState = UIState.Loading;

            if (typeName === UIManager.UIType.Page) {
                this.LoadScene(viewName, (sceneAsset: SceneAsset) => {
                    if (this.viewCommandList.indexOf(command) === -1) return;
                    this.viewCommandList.splice(this.viewCommandList.indexOf(command), 1);

                    if (!sceneAsset) {
                        this.error(`${viewName}加载Scene失败`);
                        viewSource.viewState = UIState.None;
                        command.onError && command.onError();
                        this.tryShow();
                        return;
                    }

                    director.runSceneImmediate(sceneAsset, null, (err, scene) => {
                        if (err) {
                            this.error(`${viewName}加载Scene失败`);
                            viewSource.viewState = UIState.None;
                            command.onError && command.onError();
                            this.tryShow();
                            return;
                        }
                        const rootNodes = scene.children;
                        const gameObject = rootNodes.find(node => {
                            return node.getComponent(BaseView) != null;
                        });
                        if (gameObject) {
                            const view = gameObject.getComponent(BaseView);
                            viewSource.viewState = UIState.Showing;
                            viewSource.viewComp = view;
                            this.tryShow();

                            gameObject.name = fullName;
                            BaseView.init(view, this.module);
                            BaseView.innerShow(view, command.data, command.onShow, command.onHide).then(() => {
                                UIManager.refreshShadow();
                            });
                        } else {
                            this.error(`${viewName}中未查询到BaseView组件`);
                            viewSource.viewState = UIState.None;
                            this.tryShow();
                        }
                    });
                }, command.onProgress);
            } else {
                this.LoadPrefab(viewName, (prefab) => {
                    if (this.viewCommandList.indexOf(command) === -1) return;
                    this.viewCommandList.splice(this.viewCommandList.indexOf(command), 1);

                    if (!prefab) {
                        this.error(`${viewName}加载UI失败`);
                        viewSource.viewState = UIState.None;
                        command.onError && command.onError();
                        this.tryShow();
                        return;
                    }

                    const root = app.configuration.UserInterface.getChildByName(typeName);
                    const gameObject = instantiate(prefab);
                    gameObject.parent = root;
                    gameObject.active = false;
                    gameObject.name = fullName;

                    const view = gameObject.getComponent(BaseView);
                    if (view) {
                        viewSource.viewState = UIState.Showing;
                        viewSource.viewComp = view;
                        this.tryShow();

                        BaseView.init(view, this.module);
                        BaseView.innerShow(view, command.data, command.onShow, command.onHide).then(() => {
                            UIManager.refreshShadow();
                        });
                    } else {
                        this.error(`${viewName}中未查询到BaseView组件`);
                        viewSource.viewState = UIState.None;
                        this.tryShow();
                    }
                }, command.onProgress);
            }
        }
    }

    public show<T extends typeof BaseView<M>, M extends CoreModule>(params: {
        view: T,
        data?: any,
        onShow?: () => void,
        onHide?: (data: any) => void,
        onError?: () => void,
        onProgress?: (progress: number) => void
    }): void {
        this.viewCommandList.push({
            viewType: params.view,
            data: params.data,
            onShow: params.onShow,
            onHide: params.onHide,
            onError: params.onError,
            onProgress: params.onProgress
        });
        if (this.tryShowing == false) {
            this.tryShow();
        }
    }

    public hide(view: typeof BaseView | BaseView): void {
        const viewSource = UIManager.getViewSource(view);
        if (viewSource) {
            if (viewSource.viewState === UIState.Showing) {
                if (BaseView.innerHide(viewSource.viewComp)) {
                    viewSource.viewState = UIState.Hiding;
                    if (viewSource.viewComp.hideMode === BaseView.ViewHideMode.Active) {
                        viewSource.viewComp.node.active = false;
                    } else {
                        viewSource.viewComp.node.destroy();
                        viewSource.viewComp = null;
                    }
                }
            } else {
                viewSource.viewState = UIState.Hiding;
            }
        }

        if (!(view instanceof BaseView)) {
            // 移除指令列表
            this.viewCommandList = this.viewCommandList.filter(command => {
                return command.viewType !== view;
            });
        }

        UIManager.refreshShadow();
    }

    public hideAll(type: keyof typeof UIType): void {
        UIManager.viewSourceMap.forEach(viewSourceMap => {
            viewSourceMap.forEach((viewSource, viewName) => {
                if (getUITypeName(viewName) === type) {
                    if (viewSource.viewState === UIState.Showing) {
                        if (BaseView.innerHide(viewSource.viewComp)) {
                            viewSource.viewState = UIState.Hiding;
                            if (viewSource.viewComp.hideMode === BaseView.ViewHideMode.Active) {
                                viewSource.viewComp.node.active = false;
                            } else {
                                viewSource.viewComp.node.destroy();
                                viewSource.viewComp = null;
                            }
                        }
                    } else {
                        viewSource.viewState = UIState.Hiding;
                    }
                }
            });
        });

        // 移除指令列表
        this.viewCommandList = this.viewCommandList.filter(command => {
            return getUITypeName(command.viewType.name) !== type;
        });

        UIManager.refreshShadow();
    }
}